package main

import (
	"database/sql"
	"errors"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	xerrors "github.com/pkg/errors"
	"sync"
)

const (
	USERNAME = "root"
	PASSWORD = "123456"
	IP       = "127.0.0.1"
	PORT     = "3306"
	DBNAME   = "test"
)

type Manager struct {
	DB *sql.DB
}

var once sync.Once
var manr *Manager
var ErrDbConnect = errors.New("db connect fail")
var ErrOpenDb = errors.New("opon database fail")
var ErrNotFound = errors.New("err no rows")

//单例模式
func GetDb() *Manager {
	once.Do(func() {
		path := USERNAME + ":" + PASSWORD + "@tcp(" + IP + ":" + PORT + ")/" + DBNAME
		DB, err := sql.Open("mysql", path)
		if err != nil {
			//这里可以使用panic
			//fmt.Println("db connect fail")
			panic(ErrDbConnect)
		}
		if err := DB.Ping(); err != nil {
			//这里可以使用panic
			//fmt.Println("opon database fail")
			panic(ErrOpenDb)
		}

		manr = &Manager{
			DB: DB,
		}

	})
	return manr
}

func FindAll(queryString string) ([]map[string]string, error) {
	var ret []map[string]string
	rows, err := GetDb().DB.Query(queryString)
	if err != nil {
		return ret, xerrors.Wrapf(err, "查询出错:\\nSQL:\\n%s", queryString)
	}

	//获取列名cols
	cols, err := rows.Columns()
	if err != nil {
		return ret, xerrors.Wrap(err, "table error")

	}

	for rows.Next() {
		//定义一个切片,长度是字段的个数,切片里面的元素类型是sql.RawBytes
		values := make([]sql.RawBytes, len(cols))
		//定义一个切片,元素类型是interface{} 接口
		scanArgs := make([]interface{}, len(cols))
		for i := range values {
			//把sql.RawBytes类型的地址存进去了
			scanArgs[i] = &values[i]
		}
		//扫描到scanArgsf接口中，实际是字符串类型data中
		err := rows.Scan(scanArgs...)
		if err != nil {
			//返回下自定义的错误
			if err == sql.ErrNoRows {
				return ret, xerrors.Wrap(ErrNotFound, "err no erros")
			}
			return ret, xerrors.Wrap(err, "loop faile")
		}
		dataKv := make(map[string]string, len(cols))
		for k, col := range values {
			dataKv[cols[k]] = string(col)
		}
		ret = append(ret, dataKv)
	}
	return ret, nil
}

func getComment() ([]map[string]string, error) {
	sql := "SELECT * FROM comment"

	return FindAll(sql)
}

func main() {
	//初始化数据库链接失败
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println(err)
		}
	}()

	//数据库查询为空，可以返回空
	ret, err := getComment()
	if err != nil {
		//没有查到行
		if errors.Is(err, ErrNotFound) {
			fmt.Println(err.Error())
		} else {
			//原始信息
			fmt.Printf("original error : %T %v\n", xerrors.Cause(err), xerrors.Cause(err))
			//栈信息
			fmt.Printf("stack trace : \n %+v\n", err)
		}
		return
	}

	for index, value := range ret {
		fmt.Printf("rows: %d , name： %s, star : %s \n", index, value["username"], value["star"])
	}
}
